home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 24
/
Aminet 24 (1998)(GTI - Schatztruhe)[!][Apr 1998].iso
/
Aminet
/
dev
/
lang
/
PPCcforth.lha
/
PPCcforth
/
forth.c
< prev
next >
Wrap
C/C++ Source or Header
|
1985-12-27
|
14KB
|
565 lines
/*
* forth.c
*
* Portable FORTH interpreter in C
*
* Author: Allan Pratt, Indiana University (iuvax!apratt)
* Spring, 1984
* References: 8080 and 6502 fig-FORTH source listings (not the greatest refs
* in the world...)
*
* This program is intended to be compact, portable, and pretty complete.
* It is also intended to be in the public domain, and distribution should
* include this notice to that effect.
*
* This file contains the support code for all interpreter functions.
* the file prims.c contains code for the C-coded primitives, and the
* file forth.h connects the two with definitions.
*
* The program nf.c generates a new forth.core file from the dictionary
* forth.dict, using common.h to tie it together with this program.
*/
#include <stdio.h>
#ifndef AMIGA
#include <signal.h>
#endif
#include <ctype.h> /* only for isxdigit */
#include "common.h"
#include "forth.h"
#include "prims.h" /* macro-defined primitives */
/* declare globals which are defined in forth.h */
unsigned short csp, rsp, ip, w;
short *mem;
int trace, tracedepth, debug, breakenable, breakpoint, qtermflag, forceip;
int nobuf;
FILE *blockfile;
long bfilesize;
char *bfilename; /* block file name (change with -f ) */
char *cfilename; /* core file name (change with -l ) */
char *sfilename; /* save file name (change with -s ) */
/*
----------------------------------------------------
SYSTEM FUNCTIONS
----------------------------------------------------
*/
errexit(s,p1,p2) /* An error occurred -- clean up (?) and
exit. */
{
printf(s,p1,p2);
printf("ABORT FORTH!\nDumping to %s... ",DUMPFILE);
fflush(stdout);
memdump();
puts("done.");
exit(1);
}
Callot (n) /* allot n words in the dictionary */
short n;
{
unsigned newsize;
mem[DP] += n; /* move DP */
if (mem[DP] + GULPFRQ > mem[LIMIT]) { /* need space */
newsize = mem[DP] + GULPSIZE;
if (newsize > MAXMEM && MAXMEM)
errexit("ATTEMPT TO GROW PAST MAXMEM (%d) WORDS\n",MAXMEM);
#ifdef AMIGA
/*
* Fake realloc by doing a malloc and copy to the new area.
* Since we are always just growing the area, this should work.
* Note that this has the disadvantage of requiring at least 2N
* bytes to grow an area of N bytes.
*/
{
register char *new, *out;
register char *in = mem;
register int count = mem[LIMIT];
new = out = (short *) malloc ((char *)mem, newsize*sizeof(*mem));
if (new == NULL)
errexit("REALLOC FAILED\n");
while (count-- > 0) {
*out++ = *in++;
}
free (mem);
mem = new;
}
#else
mem = (short *)realloc((char *)mem, newsize*sizeof(*mem));
if (mem == NULL)
errexit("REALLOC FAILED\n");
#endif /* AMIGA */
mem[LIMIT] = newsize;
}
}
push(v) /* push value v to cstack */
short v;
{
if (csp <= TIB_END)
errexit("PUSH TO FULL CALC. STACK\n");
mem[--csp] = v;
}
short pop() /* pop a value from comp. stack, and return
it as the value of the function */
{
if (csp >= INITS0) {
puts("Empty Stack!");
return 0;
}
return (mem[csp++]);
}
rpush(v)
short v;
{
if (rsp <= INITS0)
errexit("PUSH TO FULL RETURN STACK");
mem[--rsp] = v;
}
short rpop()
{
if (rsp >= INITR0)
errexit("POP FROM EMPTY RETURN STACK!");
return (mem[rsp++]);
}
pkey() /* (KEY) -- wait for a key & return it */
{
int c;
if ((c = getchar()) == EOF) errexit("END-OF-FILE ENCOUNTERED");
return(c);
}
pqterm() /* (?TERMINAL):
return true if BREAK has been hit */
{
if (qtermflag) {
push(TRUE);
qtermflag = FALSE; /* this influences ^C handling */
}
else push(FALSE);
}
pemit() /* (EMIT): c -- emit a character */
{
putchar(pop() & 0x7f); /* stdout is unbuffered */
}
next() /* instruction processor: control goes here
almost right away, and cycles through here
until you leave. */
/*
* This is the big kabloona. What it does is load the value at mem[ip]
* into w, increment ip, and invoke prim. number w. This implies that
* mem[ip] is the CFA of a word. What's in the CF of a word is the number
* of the primitive which should be executed. For a word written in FORTH,
* that primitive is "docol", which pushes ip to the return stack, then
* uses w+2 (the PFA of the word) as the new ip. See "interp.doc" for
* more.
*/
/*
* There is an incredible hack going on here: the SPECIAL CASE mentioned in
* the code is for the word EXECUTE, which must set W itself and jump INSIDE
* the "next" loop, by-passing the first instruction. This has been made a
* special case: if the primitive to execute is zero, the special case is
* invoked, and the code for EXECUTE is put right in the NEXT loop. For this
* reason, "EXECUTE" MUST BE THE FIRST WORD IN THE DICTIONARY.
*/
{
short p;
while (1) {
if (forceip) { /* force ip to this value -- used by sig_int */
ip = forceip;
forceip = FALSE;
}
#ifdef TRACE
if (trace) dotrace();
#endif TRACE
#ifdef BREAKPOINT
if (breakenable && ip == breakpoint) dobreak();
#endif BREAKPOINT
w = mem[ip];
ip++;
/* w, mem, and ip are all global. W is now
a POINTER TO the primitive number to
execute, and ip points to the NEXT thread to
follow. */
next1: /* This is for the SPECIAL CASE */
p = mem[w]; /* p is the actual number of the primitive */
if (p == 0) { /* SPECIAL CASE FOR EXECUTE! */
w = pop(); /* see above for explanation */
goto next1;
}
/* else */
switch(p) {
case LIT : lit(); break;
case BRANCH : branch(); break;
case ZBRANCH : zbranch(); break;
case PLOOP : ploop(); break;
case PPLOOP : pploop(); break;
case PDO : pdo(); break;
case I : i(); break;
case R : r(); break;
case DIGIT : digit(); break;
case PFIND : pfind(); break;
case ENCLOSE : enclose(); break;
case KEY : key(); break;
case PEMIT : pemit(); break;
case QTERMINAL : qterminal(); break;
case CMOVE : cmove(); break;
case USTAR : ustar(); break;
case USLASH : uslash(); break;
case AND : and(); break;
case OR : or(); break;
case XOR : xor(); break;
case SPFETCH : spfetch(); break;
case SPSTORE : spstore(); break;
case RPFETCH : rpfetch(); break;
case RPSTORE : rpstore(); break;
case SEMIS : semis(); break;
case LEAVE : leave(); break;
case TOR : tor(); break;
case FROMR : fromr(); break;
case ZEQ : zeq(); break;
case ZLESS : zless(); break;
case PLUS : plus(); break;
case DPLUS : dplus(); break;
case MINUS : minus(); break;
case DMINUS : dminus(); break;
case OVER : over(); break;
case DROP : drop(); break;
case SWAP : swap(); break;
case DUP : dup(); break;
case TDUP : tdup(); break;
case PSTORE : pstore(); break;
case TOGGLE : toggle(); break;
case FETCH : fetch(); break;
case CFETCH : cfetch(); break;
case TFETCH : tfetch(); break;
case STORE : store(); break;
case CSTORE : cstore(); break;
case TSTORE : tstore(); break;
case DOCOL : docol(); break;
case DOCON : docon(); break;
case DOVAR : dovar(); break;
case DOUSE : douse(); break;
case SUBTRACT : subtract(); break;
case EQUAL : equal(); break;
case NOTEQ : noteq(); break;
case LESS : less(); break;
case ROT : rot(); break;
case DODOES : dodoes(); break;
case DOVOC : dovoc(); break;
case ALLOT : allot(); break;
case PBYE : pbye(); break;
case TRON : tron(); break;
case TROFF : troff(); break;
case DOTRACE : dotrace(); break;
case PRSLW : prslw(); break;
case PSAVE : psave(); break;
case PCOLD : pcold(); break;
default : errexit("Bad execute-code %d\n",p); break;
}
}
}
dotrace()
{
short worka, workb, workc;
putchar('\n');
if (tracedepth) { /* show any stack? */
printf("sp: %04x (", csp);
worka = csp;
for (workb = tracedepth; workb; workb--)
printf("%04x ",(unsigned short) mem[worka++]);
putchar(')');
}
printf(" ip=%04x ",ip);
if (mem[R0]-rsp < RS_SIZE && mem[R0] - rsp > 0) /* if legal rsp */
for (worka = mem[R0]-rsp; worka; worka--) { /* indent */
putchar('>');
putchar(' ');
}
worka = mem[ip] - 3; /* this is second-to-last letter, or
the count